package io.gitee.sfch.chunmi.code.generator.service.impl;


import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import io.gitee.sfch.chunmi.code.generator.conf.CodeGeneratorConf;
import io.gitee.sfch.chunmi.code.generator.conf.EntityTypeEnum;
import io.gitee.sfch.chunmi.code.generator.conf.TemplateNameEnum;
import io.gitee.sfch.chunmi.code.generator.conf.WebTemplateNameEnum;
import io.gitee.sfch.chunmi.code.generator.entity.ColumnRef;
import io.gitee.sfch.chunmi.code.generator.entity.JavaType;
import io.gitee.sfch.chunmi.code.generator.entity.TableRef;
import io.gitee.sfch.chunmi.code.generator.service.ChunmiCodeGenerator;
import io.gitee.sfch.chunmi.code.generator.service.TableMetaService;
import io.gitee.sfch.chunmi.code.generator.service.TableMetaServiceManager;
import io.gitee.sfch.chunmi.code.generator.service.TemplateEngine;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.stereotype.Service;

import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;

@Slf4j
@Service
public class ChunmiCodeGeneratorImpl implements ChunmiCodeGenerator {

    @Resource
    private TableMetaServiceManager tableMetaServiceManager;
    @Resource
    private DataSourceProperties dataSourceProperties;
    @Resource
    private TemplateEngine templateEngine;

    @Override
    public void generate(CodeGeneratorConf generatorConf, String schemaName, String... tableNames) {
        Assert.notNull(tableNames, "tableNames must not be null");

        String driverClassName = dataSourceProperties.getDriverClassName();
        TableMetaService tableMetaService = tableMetaServiceManager.getTableMetaService(driverClassName);

        for (String tableName : tableNames) {
            this.generate(tableMetaService, generatorConf, schemaName, tableName);
        }
    }

    private void generate(TableMetaService tableMetaService, CodeGeneratorConf generatorConf, String schemaName,
                          String tableName) {
        TableRef tableRef = tableMetaService.getTableRef(schemaName, tableName);
        List<ColumnRef> columnRefs = tableMetaService.getColumnRefs(schemaName, tableName);
        Set<String> packageNames = new HashSet<>();
        this.handleColumnRef(tableMetaService, columnRefs, packageNames);

        Map<String, Object> model = new HashMap<>();
        model.put("basePackage", generatorConf.getBasePackage());
        model.put("tableName", tableName);
        model.put("tableComment", tableRef.getTableName());

        String entityName = generatorConf.getEntityNameMapper().apply(tableName);
        model.put("entityName", entityName);
        model.put("EntityName", StrUtil.upperFirst(entityName));
        model.put("moduleName", generatorConf.getModuleName());
        model.put("packageNames", packageNames);

        columnRefs = this.handleEntityType(generatorConf, columnRefs, packageNames, model);
        model.put("fields", columnRefs);

        // 后端生成
        for (TemplateNameEnum templateNameEnum : TemplateNameEnum.values()) {
            this.renderAndSaveFile(templateNameEnum, model, generatorConf);
        }
        // 前端生成
        for (WebTemplateNameEnum webTemplateNameEnum : WebTemplateNameEnum.values()) {
            this.renderWebAndSave(webTemplateNameEnum, model, generatorConf);
        }
    }

    /**
     * 使用模版引擎生成类，并进行保存
     *
     * @param templateNameEnum 模版名
     * @param model 参数模型
     * @param conf 配置
     */
    private void renderAndSaveFile(TemplateNameEnum templateNameEnum, Map<String, Object> model,
                                   CodeGeneratorConf conf) {
        if (!conf.isGenerateAdmin()) {
            return;
        }
        String packageName = StrUtil.format(templateNameEnum.getPackageTemplate(), model);
        packageName = StrUtil.replace(packageName, "..", ".");
        model.put("packageName", packageName);

        String templateName = templateNameEnum.name();
        String content = templateEngine.render(templateName, model);

        // 获取包路径
        String projectRoot = conf.getProjectRoot().replace("\\", "/");
        String absoluteProjectRoot = StrUtil.format("{}/src/main/java", projectRoot);

        String fileName = StrUtil.format(templateNameEnum.getClassName(), model);
        String subPackagePath = packageName.replace('.', '/');
        String subPackageFile = StrUtil.format("{}/{}.java", subPackagePath, fileName);

        // 把该类的路径添加到模型中
        model.put(templateName + "PackageName", packageName + "." + fileName);

        File file = FileUtil.file(absoluteProjectRoot, subPackageFile);

        log.info("项目根目录：{}", projectRoot);
        log.info("文件生成路径：{}", file.getAbsoluteFile());

        if (FileUtil.exist(file) && conf.isOverwrite()) {
            log.info("{}：文件已存在，不进行覆盖，跳过文件生成", packageName);
            return;
        }

        FileUtil.writeString(content, file, StandardCharsets.UTF_8);
    }

    /**
     * 生成Web项目中的type和api部分
     */
    private void renderWebAndSave(WebTemplateNameEnum webTemplateNameEnum, Map<String, Object> model,
                                  CodeGeneratorConf conf) {
        if (!conf.isGenerateWeb()) {
            return;
        }
        String webProjectRoot = conf.getWebProjectRoot();
        if (StrUtil.isEmpty(webProjectRoot)) {
            log.info("无前端项目根目录，跳过");
            return;
        }
        log.info("前端项目根目录：{}", webProjectRoot);

        String templateName = webTemplateNameEnum.name();
        String content = templateEngine.render(templateName, model);

        String packageName = StrUtil.format(webTemplateNameEnum.getPackageTemplate(), model);
        String fileName = StrUtil.format(webTemplateNameEnum.getFileNameTemplate(), model);

        File file = FileUtil.file(webProjectRoot, packageName + "/" + fileName + ".ts");

        log.info("前端文件生成路径：{}", file.getAbsoluteFile());
        if (FileUtil.exist(file) && conf.isOverwrite()) {
            log.info("{}：前端文件已存在，不进行覆盖，跳过文件生成", packageName);
            return;
        }
        FileUtil.writeString(content, file, StandardCharsets.UTF_8);
    }

    /**
     * 处理字段信息，向字段信息添加更多的内容
     */
    private void handleColumnRef(TableMetaService tableMetaService, List<ColumnRef> columnRefs,
                                 Set<String> packageNames) {
        Map<String, JavaType> javaTypeMap = tableMetaService.getJavaTypeMapping();
        Map<String, String> webTypeMap = tableMetaService.getWebTypeMapping();

        for (ColumnRef columnRef : columnRefs) {
            JavaType javaType = javaTypeMap.get(columnRef.getDataType());
            columnRef.setJavaType(javaType.getValue());
            columnRef.setWebType(webTypeMap.getOrDefault(columnRef.getDataType(), columnRef.getDataType()));
            columnRef.setCameCaseName(StrUtil.toCamelCase(columnRef.getColumnName().toLowerCase()));

            if (javaType.getPackageName() != null) {
                packageNames.add(javaType.getPackageName());
            }
        }
    }

    /**
     * 处理继承的父类
     */
    private List<ColumnRef> handleEntityType(CodeGeneratorConf generatorConf, List<ColumnRef> columnRefs,
                                             Set<String> packageNames, Map<String, Object> model) {
        EntityTypeEnum entityType = generatorConf.getEntityType();
        String classPackage = entityType.getClassPackage();
        if (classPackage != null) {
            packageNames.add(classPackage);
        }
        String superClass = entityType.getSuperClass();
        if (superClass != null) {
            model.put("superClass", superClass);
        }
        Predicate<ColumnRef> columnRefFilter = entityType.getColumnRefFilter();
        if (columnRefFilter != null) {
            columnRefs = columnRefs.stream().filter(columnRefFilter).collect(Collectors.toList());
        }
        return columnRefs;
    }
}
